package com.yf.course.modules.course.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.ability.Constant;
import com.yf.ability.config.dto.CfgPropDTO;
import com.yf.ability.config.enums.ConfigType;
import com.yf.ability.config.enums.ProviderType;
import com.yf.ability.config.service.CfgPropService;
import com.yf.ability.office.service.OfficeService;
import com.yf.ability.upload.providers.local.config.LocalConfig;
import com.yf.ability.upload.providers.local.service.LocalUpService;
import com.yf.ability.upload.providers.oss.service.OssService;
import com.yf.ability.upload.providers.qiniu.service.QiniuService;
import com.yf.base.api.api.dto.PagingReqDTO;
import com.yf.base.api.api.utils.FileUtils;
import com.yf.base.api.exception.ServiceException;
import com.yf.base.utils.BeanMapper;
import com.yf.course.enums.CourseFileType;
import com.yf.course.job.VideoDurationJob;
import com.yf.course.modules.course.dto.CourseFileDTO;
import com.yf.course.modules.course.dto.KnowLageCountDTO;
import com.yf.course.modules.course.dto.KnowladgeDTO;
import com.yf.course.modules.course.dto.ext.CourseRefDirExtDTO;
import com.yf.course.modules.course.dto.ext.CourseRefFileExtDTO;
import com.yf.course.modules.course.dto.request.CourseFileFetchReqDTO;
import com.yf.course.modules.course.dto.request.CourseFileReqDTO;
import com.yf.course.modules.course.entity.CourseFile;
import com.yf.course.modules.course.entity.CourseRefDir;
import com.yf.course.modules.course.entity.CourseRefFile;
import com.yf.course.modules.course.mapper.CourseFileMapper;
import com.yf.course.modules.course.service.CourseFileLearnService;
import com.yf.course.modules.course.service.CourseFileService;
import com.yf.course.modules.course.service.CourseRefDirService;
import com.yf.job.service.JobService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * 课件信息业务实现类
 * </p>
 *
 * @author 聪明笨狗
 * @since 2020-12-17 11:28
 */
@Log4j2
@Service
public class CourseFileServiceImpl extends ServiceImpl<CourseFileMapper, CourseFile> implements CourseFileService {

    @Autowired
    private OssService ossService;

    @Autowired
    private OfficeService officeService;

    @Autowired
    private QiniuService qiniuService;

    @Autowired
    private CfgPropService cfgPropService;

    @Autowired
    private LocalUpService localUpService;

    @Autowired
    private CourseFileLearnService courseFileLearnService;

    @Autowired
    private JobService jobService;

    @Autowired
    private CourseFileMapper courseFileMapper;

    @Autowired
    private CourseRefDirService courseRefDirService;

    @Override
    public IPage<CourseFileDTO> paging(PagingReqDTO<CourseFileReqDTO> reqDTO) {


        //查询条件
        QueryWrapper<CourseFile> wrapper = new QueryWrapper<>();

        // 请求参数
        CourseFileReqDTO params = reqDTO.getParams();

        if(params!=null){
            if(!StringUtils.isBlank(params.getCatId())){
                wrapper.lambda().eq(CourseFile::getCatId, params.getCatId());
            }

            if(!StringUtils.isBlank(params.getFileType())){
                wrapper.lambda().eq(CourseFile::getFileType, params.getFileType());
            }

            if(!StringUtils.isBlank(params.getTitle())){
                wrapper.lambda().like(CourseFile::getTitle, params.getTitle());
            }

            //排除不是当前用户的
            if(!StringUtils.isBlank(params.getCreateBy())){
                wrapper.lambda().eq(CourseFile::getCreateBy, params.getCreateBy());
            }

            if(!CollectionUtils.isEmpty(params.getExcludes())){
                //此处是排除文件，因为可能已经有文件在列表中了，已经在的不需要返回
                wrapper.lambda().notIn(CourseFile::getId, params.getExcludes());
            }
        }

        wrapper.lambda().orderByDesc(CourseFile::getCreateTime);

        //获得数据
        IPage<CourseFile> page;

        if (reqDTO.getParams() == null || reqDTO.getParams().getCourseId() == null || StringUtils.isBlank(reqDTO.getParams().getCourseId())) {
            // 无查询参数或传入课程ID或者课程ID为空字符串
            page = this.page(reqDTO.toPage(), wrapper);
        } else {
            page = courseFileMapper.selectByCourseIdPage(reqDTO.toPage(), reqDTO.getParams());
        }
        //转换结果
        IPage<CourseFileDTO> pageData = JSON.parseObject(JSON.toJSONString(page),
                new TypeReference<Page<CourseFileDTO>>() {
        });
        return pageData;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public CourseFileDTO save(CourseFileDTO reqDTO) {
        //复制参数
        CourseFile entity = new CourseFile();
        BeanMapper.copy(reqDTO, entity);
        // 预览默认原地址
        entity.setViewUrl(entity.getFileUrl());

        // 获取文件大小
        String path = entity.getFileUrl();

        // 非http开头则为本地存储
        if(!path.startsWith("http://") && !path.startsWith("https://")){
            LocalConfig conf = localUpService.getConfig();
            path = this.getRealPath(conf, path);
        }

        String fileSize = FileUtils.getFileSize(path);
        entity.setFileSize(fileSize);

        this.saveOrUpdate(entity);

        // 转换文档
        try {
            this.convert(entity);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("在转码时出现问题："+e.getMessage());
        }

        // 回写ID
        reqDTO.setId(entity.getId());
        return reqDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSave(List<CourseFile> courseFiles) {
        try {
            this.updateBatchById(courseFiles);
        }catch (Exception e) {
            throw new ServiceException("课程文件批量保存失败"+e.getMessage());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<String> ids) {

        // 删除
        this.removeByIds(ids);

        // 删除课件学习记录
        courseFileLearnService.deleteByFile(ids);
    }

    @Override
    public List<CourseRefDirExtDTO> fetchByCatId(CourseFileFetchReqDTO reqDTO) {
        List<CourseRefDirExtDTO> dirList = baseMapper.fetchByCatId(reqDTO);

        // 排除已经在列表中的数据
        for (int i=dirList.size()-1;i>=0;i--){
            CourseRefDirExtDTO dir = dirList.get(i);
            String catId = dir.getId();
            List<CourseRefFileExtDTO> fileList = baseMapper.selectFile(catId, reqDTO.getExcludes());
            if(CollectionUtils.isEmpty(fileList)){
                dirList.remove(i);
                continue;
            }

            dir.setFileList(fileList);
        }

        return dirList;
    }

    /**
     * 更新课件上传状态
     * @param reqDTO
     */
    @Override
    public void updateFileStatus(CourseFileDTO reqDTO) {
        List<CourseFileDTO> courseFileDTOS = courseFileMapper.selectByUploadId(reqDTO);
        //更新状态
        for (CourseFileDTO courseFileDTO : courseFileDTOS){
            courseFileDTO.setFileStatus(reqDTO.getFileStatus());
            courseFileDTO.setFileSize(reqDTO.getFileSize());
            courseFileDTO.setDuration(reqDTO.getDuration());
            courseFileMapper.updateFileStatus(courseFileDTO);
        }
    }

    /**
     * 批量添加知识点
     * @param knowledgeDTO
     */
    @Override
    public void addKnowledgePoint(KnowladgeDTO knowledgeDTO) {
        String classId= knowledgeDTO.getClassId();
        List<KnowLageCountDTO> content = knowledgeDTO.getContent();
        List<CourseRefDir> saveDirs = new ArrayList<>();
        content.stream().forEach(item->{
            CourseRefDir dir = new CourseRefDir();
            dir.setTitle(item.getPoint());
            dir.setDesKnowledge(item.getText());
            dir.setSort(1);
            dir.setCourseId(classId);
            dir.setId(UUID.randomUUID().toString().replace("-",""));
            saveDirs.add(dir);
        });
        // 保存目录
        courseRefDirService.saveNewItem(classId, saveDirs);
    }

    /**
     * 转换Office文档为pdf格式
     * @param entity
     * @throws Exception
     */
    private void convert(CourseFile entity) throws Exception{

        // 配置信息
        CfgPropDTO cfg = cfgPropService.detail(ConfigType.UPLOAD, "");

        // 只转换Office类型
        String fileType = entity.getFileType();


        // Office类型文档转换
        if(CourseFileType.OFFICE.equals(fileType)){
            this.convertDoc(cfg.getProvider(), entity);
            return;
        }

        // 视频类型转换
        if(CourseFileType.VIDEO.equals(fileType)){
            this.convertVideo(cfg.getProvider(), entity);
            return;
        }
    }


    /**
     * 转换文档类型数据
     * @param provide
     * @param entity
     * @throws ClientException
     */
    private void convertDoc(String provide, CourseFile entity) throws ClientException {

        // 本地上传
        if(ProviderType.LOCAL.equals(provide)){

            LocalConfig conf = localUpService.getConfig();

            // 相对文件路径
            String url = entity.getFileUrl();

            log.info("+++++input url: {}", url);

            // 变成物理路径
            String path = this.getRealPath(conf, url);

            log.info("+++++final path: {}", path);

            // 目标地址
            String dist = path+".pdf";

            // 返回物理路径
            String rest = officeService.convert(path, dist);

            log.info("+++++convert rest: {}", rest);

            // 再变回访问路径
            String result = url + ".pdf";

            entity.setViewUrl(result);
            this.updateById(entity);
            return;
        }

        // OSS上传
        if(ProviderType.ALIYUN.equals(provide)) {
            // 阿里云文档转换
            String url = ossService.convertDoc(entity.getFileUrl());
            entity.setViewUrl(url);
            this.updateById(entity);
            return;
        }



        // 七牛云上传
        if(ProviderType.QINIU.equals(provide)) {
            // 七牛云旧版直接添加配置即可
            String url = entity.getFileUrl()+"?doc-convert/preview";
            entity.setViewUrl(url);
            this.updateById(entity);
            return;
        }

        // COS文件上传
        if(ProviderType.QCLOUD.equals(provide)) {
            // 在线文件处理
            String url = entity.getFileUrl()+"?ci-process=doc-preview&dstType=pdf";
            entity.setViewUrl(url);
            this.updateById(entity);
            return;
        }
    }


    /**
     * 转换视频
     * @param provide
     * @param entity
     * @throws ClientException
     */
    private void convertVideo(String provide, CourseFile entity) throws ClientException {

        String viewUrl = "";

        // 本地上传
        if(ProviderType.LOCAL.equals(provide)){
            viewUrl = entity.getFileUrl();
        }

        // OSS上传
        if(ProviderType.ALIYUN.equals(provide)) {
            viewUrl = ossService.convertVideo(entity.getFileUrl());
        }

        // 七牛云上传
        if(ProviderType.QINIU.equals(provide)) {
            // 转换视频并返回
            viewUrl = qiniuService.convertVideo(entity.getFileUrl());
            return;
        }

        // COS 视频转码
        if(ProviderType.QCLOUD.equals(provide)) {
            // 转换视频并返回
            // TODO 腾讯云COS转码
            return;
        }

        // 保存转码地址
        if(StringUtils.isNotBlank(viewUrl)){
            entity.setViewUrl(viewUrl);
            this.updateById(entity);

            // 时长为0时，系统读取
            if(entity.getDuration()==null || entity.getDuration().equals(0)){

                // 进行时间读取
                CourseFileDTO dto = new CourseFileDTO();
                dto.setId(entity.getId());
                dto.setViewUrl(viewUrl);

                jobService.addCronJob(VideoDurationJob.class, null, JSON.toJSONString(dto));
            }

        }
    }

    /**
     * 获取真实物理文件地址
     * @param uri
     * @return
     */
    public String getRealPath(LocalConfig conf, String uri){

        String regx = Constant.FILE_PREFIX+"(.*)";

        // 查找全部变量
        Pattern pattern = Pattern.compile(regx);
        Matcher m = pattern.matcher(uri);
        if (m.find()) {
            String str = m.group(1);
            return conf.getLocalDir() + str;
        }

        return null;
    }



}
